using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace Nice157Advices.Advices
{
    /// <summary>
    /// 14、正确实现浅拷贝和深拷贝
    /// </summary>
    public static class _14
    {
        public static void Go ()
        {
            var employee = new Employee ()
            {
                IDCode = "12345",
                Age = 24,
                Department = new Department { Name = "玉观邸" }
            };
            System.Console.WriteLine ($"{employee.IDCode} {employee.Age} {employee.Department.Name}");

            var another = employee.Clone () as Employee;
            employee.IDCode = "6789";
            employee.Age = 22;
            employee.Department.Name = "华龙家园";
            /*
            Employee的IDCode属string类型。理论上string类型是引用类型，但是由于该引用类型的特殊性（无论是实际还是语义），Object.MemberwiseClone方法仍旧为其创建了副本。也就是说，在浅拷贝过程，我们应该将字符串看成是值类型。Employee的Department属性是一个引用类型，所以，如果改变了源对象Niki中的值，那么副本Kris中的值也会随之一起变动。
             */
            System.Console.WriteLine ($"{another.IDCode} {another.Age} {another.Department.Name}");


            // 深拷贝
            Employee2 employee2 = new Employee2 ()
            {
                IDCode = "12345",
                Department = new Department2 { Name = "玉观邸" }
            };
            System.Console.WriteLine ($"{employee2.IDCode} {employee2.Department.Name}");
            var another2 = employee2.Clone () as Employee2;
            employee2.IDCode = "6789";
            employee2.Department.Name = "华龙家园";
            System.Console.WriteLine ($"{another2.IDCode} {another2.Department.Name}");

        }

        private class Employee : ICloneable
        {
            public string IDCode { get; set; }
            public int Age { get; set; }
            public Department Department { get; set; }

            public object Clone () => this.MemberwiseClone ();

        }
        private class Department
        {
            public string Name { get; set; }
            public override string ToString () => this.Name;
        }

        /**
         *  Employee的深拷贝有多种实现方法，最简单的方式是手动的对字段进行逐个的赋值。但是这种方法容易出错，也就是说，如果类型的字段发生变化或有增减，那么该拷贝方法也要发生相应的变化，所以，建议使用序列化的形式来进行深拷贝。Employee深拷贝的一种实现方式如下：
         */

        [Serializable]
        private class Employee2 : ICloneable
        {
            public string IDCode { get; set; }
            public Department2 Department { get; set; }

            public object Clone ()
            {
                // 深拷贝
                using (Stream objectStream = new MemoryStream ())
                {
                    IFormatter formatter = new BinaryFormatter ();
                    formatter.Serialize (objectStream, this);
                    objectStream.Seek (0, SeekOrigin.Begin);
                    return formatter.Deserialize (objectStream) as Employee2;
                }
            }
        }

        [Serializable]
        private class Department2
        {
            public string Name { get; set; }
            public override string ToString() => this.Name;
        }

    }
}